/*
 * client_handler.cpp
 *
 *  Created on: 17.01.2012
 *      Author: knz
 */

#include "client-handler.h"

#include "app-shared.h"

#include <sys/types.h>
#include <sys/socket.h>
#include <sstream>
#include <cstring>
#include <cerrno>

#include "application.h"

//===================================================================
//
// ClientJsonListener
//
//===================================================================

ClientJsonListener::ClientJsonListener(ClientHandler *obj)
    : m_pMaster(obj)
{ }

void ClientJsonListener::readyRead(JsonValuesMap *val)
{
    unsigned short mask = 0;

    int id = 0;
    int idDest = 0;
    std::string sMsg;
    std::string sCmd;

    if (val->isIntValue("id")) {
        mask |= m_bId;
        id = val->getIntValue("id");
    }

    if (val->isIntValue("to")) {
        mask |= m_bDestId;
        idDest = val->getIntValue("to");
    }

    if (val->isStrValue("msg")) {
        mask |= m_bMsg;
        sMsg = val->getStrValue("msg");
    }

    if (val->isStrValue("cmd")) {
        mask |= m_bCmd;
        sCmd = val->getStrValue("cmd");
    }

    switch (mask) {
    case m_bgrId:
        // Получено сообщение типа {"id":10}
        // - мой ID = 10
        IF_DEBUG_LEVEL
                LV_DEBUG("[Client %d] id = %d", m_pMaster->descriptor(), id);
        m_pMaster->setId(id);
        g_pApp->registerClientId(id, m_pMaster);
        break;
    case m_bgrMsg:
        // Получено сообщение типа {"to":20, "msg":"Hello, my friend"}
        // - для ID = 20 передать msg = "Hello, my friend"
        IF_DEBUG_LEVEL
                LV_DEBUG("[Client %d] to: %d, msg: %s", m_pMaster->descriptor(),
                         idDest, sMsg.c_str());
        g_pApp->sendMsg(m_pMaster->id(), idDest, sMsg);
        break;
    case m_bgrCmd:
        // Получено сообщение типа {"cmd":"quit"}
        // - остановить сервер (удобно для выключения демона)
        IF_DEBUG_LEVEL
                LV_DEBUG("[Client %d] cmd: %s", m_pMaster->descriptor(),
                         sCmd.c_str());
        if (sCmd == "quit") {
            g_saStopFlag = 1;
        }
        break;
    case m_bgrHistById:
        // Получено сообщение типа {"cmd":"hist", "id":20}
        // - Получить историю по собеседнику с ID = 20
        IF_DEBUG_LEVEL
                LV_DEBUG("[Client %d] history by id: %d", m_pMaster->descriptor(),
                         id);
        if (sCmd == "hist") {
            g_pApp->sendHistory(m_pMaster, id);
        }
        break;
    }
}

void ClientJsonListener::parseError(std::string sError)
{
    IF_ERR_LEVEL
            LV_ERR("[ClientHandler %d] Parse error: %s",
                   m_pMaster->m_iDescriptor,
                   sError.c_str());
}

//===================================================================
//
// ClientHandler
//
//===================================================================

ClientHandler::ClientHandler(int iClientDescriptor, std::string sClientIpAddress)
{
    m_iDescriptor = iClientDescriptor;
    m_sClientIpAddress = sClientIpAddress;
    m_id = -1; // идентификатор клиента не определен при создании объекта

    m_iInBufSize = 1024;
    m_pchInBuf = new char[m_iInBufSize+1];  // +1 чтобы иметь возможность зануления указателя

    IF_DEBUG_LEVEL
    LV_DEBUG("ClientHandler with descriptor = %d was created.", m_iDescriptor);

    // не более 8 килобайт за цикл приема данных
    m_iMaxDataSize = 8*1024;

    // добавим делегата на получение данных в JSON-парсер
    m_pListener = new ClientJsonListener(this);
    m_parser.addListener(m_pListener);
}

ClientHandler::~ClientHandler()
{
    IF_DEBUG_LEVEL
    LV_DEBUG("ClientHandler with descriptor = %d will be deleted.", m_iDescriptor);

    if (m_iDescriptor >= 0) close(m_iDescriptor);
}

void ClientHandler::printInBuffer(const char *pchInBuf, int bufLen)
{
    const char *buf = pchInBuf;
    while(bufLen>0) {
        int len = strlen(buf);
        if (len > 0) {
            LV_DEBUG("recv() from client: %d bytes: %s", len, buf);
        } else break;
        bufLen = bufLen-len-1;
        buf = buf+len+1;
    }
}

ExecCode ClientHandler::execAtom()
{
    int iDataSize = 0;
    while(1) {

        int rd_len = recv(m_iDescriptor, m_pchInBuf, m_iInBufSize, MSG_NOSIGNAL);
        if (rd_len<0) {
            IF_ERR_LEVEL
            LV_ERR("[ClientHandler::execAtom()] Client will be distroyed by error '%s' "
            		"(descriptor=%d)", strerror(errno), m_iDescriptor);
            return EC_ERROR;
        }
        if (rd_len == 0) {
            IF_NOTICE_LEVEL
            LV_NOTICE("[ClientHandler::execAtom()] Client disconnected (descriptor=%d)",
                      m_iDescriptor);
            return EC_DISCONNECT;
        }

        iDataSize += rd_len;
        IF_DEBUG_LEVEL {
            // занулим буфер полученных данных перед выводом на печать
            // (специально для этого был распределен лишний байт в буфере)
            m_pchInBuf[rd_len] = 0;
            printInBuffer(m_pchInBuf, rd_len);
        }

        // обработка прочитанного буфера m_pchInBuf длиной rd_len
        m_parser.parse(m_pchInBuf, rd_len);

        // если буфер заполнен не до конца, значит забрали остатки
        // данных и можно передать управление другим подзадачам
        if (rd_len<m_iInBufSize) break;

        // если размер непрерывно получаемых данных превосходит некоторый
        // отведенный порог, то остановим выборку данных, но сообщим
        // серверу, что желательно продолжить выборку, по возможности,
        // как можно скорее
        if (iDataSize>=m_iMaxDataSize) {
            return EC_AGAIN;
        }
    }

    return EC_OK;
}

/** Сформировать JSON-объект для отправки сообщения
  *
  * @todo Реализовать возможность отправки данных частями, так как
  * существует вероятность того, что за одно обращение к send()
  * будет отправлена только часть данных.
  */
void ClientHandler::sendJsonMsg(std::string sMsg)
{
    std::stringstream ss;

    ss << "{ \"to\":" << m_id << ", \"msg\":\"" << sMsg << "\"}";
    std::string s = ss.str();

    IF_DEBUG_LEVEL
            LV_DEBUG("[Client %d] Send message: %s", m_id, s.c_str());

    int res = send(m_iDescriptor, s.c_str(), s.length(), MSG_NOSIGNAL);
}

/** Сформировать JSON-объект для отправки элемента истории
  *
  * @todo Реализовать возможность отправки данных частями, так как
  * существует вероятность того, что за одно обращение к send()
  * будет отправлена только часть данных.
  */
void ClientHandler::sendJsonHistory(int idFrom, const std::string &sMsg)
{
    std::stringstream ss;

    ss << "{ \"from\":" << idFrom << ", \"hist\":\"" << sMsg << "\"}";
    std::string s = ss.str();

    IF_DEBUG_LEVEL
            LV_DEBUG("[Client %d] Send history: %s", m_id, s.c_str());

    int res = send(m_iDescriptor, s.c_str(), s.length(), MSG_NOSIGNAL);
}
